//animation example 3
//use the full framework to initialise allegro, load animations and configuration
//also allows us to set things like triple buffering, video bitmaps, etc.
//only changes from examples 1 and 2 are here, refer to these documents for their help

#include "../../../axl_framework.h"	//this opens axl_config.h/axl_animation.h for us

using namespace AXL_Projects;

//main game variables
static Framework* GameFramework;			//framework
static AnimationLibrary* GameLibrary;		//our graphics library
static Configuration* GameConfiguration;	//our configuration object
static Animation* MainAnimation;			//our animation item
static Animation* CollisionAnimation;		//for collision detection
static Animation* LogoAnimation;			//the logo grabbed as a standard animation object
											//useful for when we want to use the animation stuff like timers, etc.
static BITMAP* LogoAnimationSimple;		//grabbed directly without creating any new object and new animation stuff is available
static Animation* SpawnedAnimations[40];		//for showing lots of them
static int speed;
static int w,h,logow,logoh;
static int CollisionTest=0;
static bool ShowSpawned=false;
static int coll_x=503;
static int coll_y=218;

extern volatile int TimerCurrentFPS;	//fps value to report as the fPS - updated every second from TimerFrameCount

//forward declarations to avoid having a header file

//our logic code during the frame loop - paired with drawing
bool LogicGame();
//our drawing code during the frame loop - paired with logic
bool DrawingGame();
void SetupGame();
void EndGame();
void DoMenu(int speed);

int main(void)
{
	//create our framework
	//this loads up and initialises the animation and graphics (same as creating an animation object and calling the load method)
	//it also loads up the configuration details (same as creating a configuration object)
	//this particular constructor also automatically initialises allegro (same as calling allegro start method from configuration module)
	//this particular constructor also initialises all the other framework stuff, like video buffers for paging/triple buffering
	// and if set in the config.xml file, will ensure the loaded graphics are the same type as the drawing buffer
	// quite a lot for a little function ;)
	std::string config;
	std::string animation;

	//normal way is to use files, but can mix and match files with actual XML
	//below is files
	config="config.xml";
	animation="animations.xml";

	//to change to xml, just use the below
	/*config = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
	"<config>"
	"    <system fps=\"60\" autowritemain=\"0\" autowritecustom=\"0\" "
	"		enablejoystick=\"0\" enablekeyboard=\"1\" enablemouse=\"1\" enablesound=\"1\" enablemidi=\"1\""
	"		matchrefreshrate=\"1\""
	"	/>"
	"    <graphics vsync=\"0\" graphicsmode=\"0\" depthpreferred=\"32\" depthfallback=\"16\" capbmptype=\"0\""
	"	/>"
	"    <window width=\"640\" height=\"480\" autodetect=\"windowed\" />"
	"    <sound maxvoicearray=\"32\" samplevolume=\"150\" musicvolume=\"128\" />"
	"    <custom name=\"message\" valuestring=\"Hi Mam\" />"
	"</config>";

	animation="<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
		"<animations>"
		"	<global "
		"		loop=\"reverserepeat\""
		"		millisecondwait=\"100\""
		"		depth=\"32\""
		"        sheetgrid=\"0\" isasheetgraphic=\"1\""
		"		ppmask=\"1\""
		"		bmp_type=\"1\""
		"	/>"
		"	<graphic "
		"		id=\"master\" "
		"		bmp_file=\"sheet.bmp\" "
		"		isasheetgraphic=\"0\" "
		"	/>"
		"	<graphic "
		"		id=\"logo\" "
		"		bmp_file=\"logo.bmp\" "
		"		isasheetgraphic=\"0\" "
		"	/>"
		"	<animation id=\"ufo\" "
		"		bmp_file=\"sheet.bmp\" "
		"		loop=\"start\""
		"		sheetitemwidth=\"32\" sheetitemheight=\"32\" "
		"		isasheetgraphic=\"1\" "
		"	>"
		"		<animationitem tilex=\"0\" />"
		"		<animationitem tilex=\"1\" tiley=\"0\" />"
		"		<animationitem tilex=\"2\" />"
		"		<animationitem tilex=\"3\" />"
		"		<animationitem tilex=\"4\" />"
		"		<animationitem tilex=\"5\" />"
		"		<animationitem tilex=\"6\" />"
		"		<animationitem tilex=\"7\" />"
		"	</animation>"
		"	<animation id=\"fuel\" "
		"		bmp_file=\"sheet.bmp\"		"
		"		originx=\"0\" originy=\"32\" "
		"		sheetitemwidth=\"24\" sheetitemheight=\"24\" "
		"		isasheetgraphic=\"1\" "
		"		sequencecount=\"4\""
		"	/>"
		"</animations>";
	*/
	GameFramework=new Framework(config,animation);
	if(Configuration::GlobalErrorString!="")
	{
		delete GameFramework;
		allegro_message(Configuration::GlobalErrorString.c_str());
	}
	else
	{
		//initialise some some first
		SetupGame();

		//this function invokes the FPS based timer loop. the two parameters are our functions that
		//the framework will call at the correct time to match our required FPS
		//refer to the help file for the parameter list
		if( !GameFramework->StartGameLoop(
				NULL,NULL,
				NULL,NULL,
				LogicGame,DrawingGame,
				NULL,NULL,true) )
		{
			delete GameFramework;
			allegro_message("Failed to run game loop. Check config.log");
		}
		else
		{
			//when the program is exited (ESCape pressed)
			//endgame will destroy all the stuff we created
			EndGame();
			//this will destroy all animations and configurations, and shut down allegro
			delete GameFramework;
		}
	}
	allegro_exit();
	return 0;
}
END_OF_MAIN();

//this function is the same as the code in the previous two examples, except now it is not
//inside a timer loop. It is called automatically by the framework
//it does all the non-drawing stuff, i.e. draws the menu, handles keyboard, etc
//an addition menu option 'D' is available that will reset the system. this shows how you would allow users to
//change the configuration, e.g. within an options menu
//also, ESC has been removed from the loop because we have asked the Framework to handle it (see StartGameLoop)
bool LogicGame()
{
	static bool CanMove=false;
	static bool FirstSprite=true;

	int keypress;
	CanMove=false;	//manual mode only
	if(keypressed())
	{
		keypress=readkey()>>8;
		switch(keypress)
		{
		case KEY_1:
			MainAnimation->SetNewLoopType(LOOP_ONCE);
			break;
		case KEY_2:
			MainAnimation->SetNewLoopType(LOOP_REVERSEREPEAT);
			break;
		case KEY_3:
			MainAnimation->SetNewLoopType(LOOP_START);
			break;
		case KEY_4:
			MainAnimation->SetNewLoopType(LOOP_REVERSEONCE);
			break;
		case KEY_5:
			MainAnimation->SetNewLoopType(LOOP_REVERSEBACK);
			break;
		case KEY_6:
			MainAnimation->Pause();
			break;
		case KEY_7:
			MainAnimation->Resume();
			break;
		case KEY_8:
			MainAnimation->SetAutoMode(true);
			break;
		case KEY_9:
			MainAnimation->SetAutoMode(false);
			break;
		case KEY_0:
			MainAnimation->Reset();
			break;
		case KEY_S:
			if(FirstSprite) MainAnimation->SetNewAnimation("ufo");
			else MainAnimation->SetNewAnimation("fuel");
			FirstSprite=!FirstSprite;
			break;
		case KEY_T:
			if(!SpawnedAnimations[0])
			{
				for(int i=0;i<40;++i)
				{
					if(i%2) SpawnedAnimations[i]=new Animation(GameLibrary,"fuel");
					else SpawnedAnimations[i]=new Animation(GameLibrary,"ufo");
					SpawnedAnimations[i]->SetNewSpeedMS(rand()%150+50);
				}
			}
			ShowSpawned=true;
			break;
		case KEY_B:
			MainAnimation->FadeOut(5000,true,makecol(0,0,0));
			break;
		case KEY_C:
			MainAnimation->FadeIn(5000,true,makecol(0,0,0));
			break;

		//new functionality. restart the graphics system
		//we need to delete our own graphics and memory first, as this method will stop allegro and restart it
		case KEY_D:
			//aborts if fail
			//but passes back fail if animations exist but not initialised via a XML file
			//- constructors are not supported for this method
			//all pointers to parts of the configuration, animation and framework must be deleted
			//all animations, etc. must be destroyed
			Configuration::LogEntry("restarting",false,"test.log");
			EndGame();	//destroy our animations and pointers
			GameFramework->RestartSystem();
			SetupGame();	//redo them all
			break;
		case KEY_SPACE:
			CanMove=true;
			break;
		case KEY_MINUS:
			speed-=10;
			MainAnimation->SetNewSpeedMS(speed);
			break;
		case KEY_EQUALS:
			speed+=10;
			MainAnimation->SetNewSpeedMS(speed);
			break;
		case KEY_F1:
			MainAnimation->SetCustomCountDown(1,5000);
			break;
		case KEY_F2:
			MainAnimation->SetCustomCountDown(2,(rand()%5+5)*1000);
			break;
		case KEY_F3:
			MainAnimation->SetCustomCountDown(3,(rand()%5+5)*1000);
			break;
		case KEY_F4:
			MainAnimation->SetCustomCountDown(4,(rand()%5+5)*1000);
			break;
		//movement of collision
		case KEY_UP:
			if(coll_y>235) coll_y--;
			break;
		case KEY_DOWN:
			if(coll_y<460) coll_y++;
			break;
		case KEY_LEFT:
			if(coll_x>500) coll_x--;
			break;
		case KEY_RIGHT:
			if(coll_x<600) coll_x++;
			break;

		//no need as we have said ESCape can abort the program, but doing it anyway
		//just in case we want to do any extra cleanup
		/*case KEY_ESC:
			return true;
			break;*/
		}
		clear_keybuf();
	}

	//collision test
	//CollisionTest=check_bb_collision(CollisionAnimation->ReadOnly_CurrentPPCOLMask,MainAnimation->ReadOnly_CurrentPPCOLMask,coll_x,coll_y,570,270);
	CollisionTest=CollisionAnimation->CheckCollision(coll_x,coll_y, MainAnimation->ReadOnly_CurrentPPCOLMask, 570,270);
	if(MainAnimation->GetAutoMode()) MainAnimation->NextMove();
	else
	{
		//if manual mode then do full move this frame if space pressed
		//otherwise just update the timers for the fade/countdown as we still
		//need these to work
		if(CanMove) MainAnimation->NextMove();
		else MainAnimation->ManualTimerUpdate();
	}
	return false;
}

//this function is the same as the code in the previous two examples, except now it is not
//inside a timer loop. It is called automatically by the framework
//it does all the drawing stuff, i.e. draws the menu, handles keyboard, etc
//the drawing buffers are provided by the framework so we need not worry about whether we are using
//paging, double or triple buffering, etc.
bool DrawingGame()
{
	//clear our drawing buffer
	clear_to_color(GameFramework->DrawingSurface,makecol(0,0,0));

	rect(GameFramework->DrawingSurface,500,210,639,479,makecol(255,0,0));
	blit(LogoAnimation->ReadOnly_CurrentItem,GameFramework->DrawingSurface,0,0,400,0,logow,logoh);
	blit(LogoAnimationSimple,GameFramework->DrawingSurface,0,0,400,70,logow,logoh);
	DoMenu(MainAnimation->GetCurrentSpeedMS());
	stretch_sprite(GameFramework->DrawingSurface, MainAnimation->ReadOnly_CurrentItem, 100,240,w*2,h*2);

	//our stuff for collision
	//stretch_blit(MainAnimation->ReadOnly_CurrentItem,GameFramework->DrawingSurface,0,0,MainAnimation->ReadOnly_CurrentItem->w,MainAnimation->ReadOnly_CurrentItem->h,470,270,MainAnimation->ReadOnly_CurrentItem->w,MainAnimation->ReadOnly_CurrentItem->h);
	draw_sprite(GameFramework->DrawingSurface,MainAnimation->ReadOnly_CurrentItem,570,270);
	draw_sprite(GameFramework->DrawingSurface,CollisionAnimation->ReadOnly_CurrentItem,coll_x,coll_y);
	if(CollisionTest==1)
	{
		textprintf_ex(GameFramework->DrawingSurface,font,501,221,makecol(255,255,255),-1,"Bounding Only");
		rect(GameFramework->DrawingSurface,coll_x,coll_y,coll_x+CollisionAnimation->ReadOnly_CurrentItem->w,coll_y+CollisionAnimation->ReadOnly_CurrentItem->h,makecol(0,255,0));
		rect(GameFramework->DrawingSurface,570,270,570+MainAnimation->ReadOnly_CurrentItem->w,270+MainAnimation->ReadOnly_CurrentItem->h,makecol(0,0,255));
	}
	else if(CollisionTest==2)
	{
		textprintf_ex(GameFramework->DrawingSurface,font,501,221,makecol(255,255,255),-1,"Pixel Perfect!");
	}

	//spawned
	if(ShowSpawned)
	{
		for(int j=0;j<4;++j)
		{
			for(int k=0;k<10;++k)
			{
				SpawnedAnimations[j*10+k]->NextMove();
				draw_sprite(GameFramework->DrawingSurface,SpawnedAnimations[j*10+k]->ReadOnly_CurrentItem,k*50,300+j*50);
			}
		}
	}
	return false;
}


/**********************************************************************************
 our helper functions, the same as the animation functions, but using the framework
*/
void SetupGame()
{
	srand ( time(NULL) );
	SpawnedAnimations[0]=NULL;	//quickly show not set up yet
	ShowSpawned=false;

	//after the framework is initialised we need to access the animations and configuration objects
	//the objects returned are the same as those if we created configuration and animation objects ourselves as in example 2
	GameLibrary=GameFramework->GetGraphicsLibrary();
	GameConfiguration=GameFramework->GetConfiguration();

	//GameLibrary->draw();
	//get animation via a string
	MainAnimation=new Animation(GameLibrary,"ufo");
	CollisionAnimation=new Animation(GameLibrary,"fuel");

	//get animation the via the library just to show how it is done
	AnimationLibraryFrames* af=GameLibrary->GetAnimation("logo");
	//this method below is quicker than the string version if the above is called outside of any critical loop
	LogoAnimation=new Animation(GameLibrary,af);
	//this version is just to grab it directly as this assumes no animation methods are required
	LogoAnimationSimple=GameLibrary->GetFirstGraphic("logo");
	MainAnimation->GetStandardDimensions(&w,&h);
	LogoAnimation->GetStandardDimensions(&logow,&logoh);
	speed=MainAnimation->GetCurrentSpeedMS();

	//GameLibrary->draw();
	GameLibrary->DebugData();

	Configuration::LogEntry("initialised",false,"test.log");
}

void EndGame()
{
	//only delete the stuff we create
	//the framework deletes the drawing buffers, animation, framework, configuration
	for(int l=0;l<40;++l)
		delete SpawnedAnimations[l];

	delete MainAnimation;
	delete CollisionAnimation;
	Configuration::LogEntry("terminated",false,"test.log");

}


void DoMenu(int speed)
{
	switch(GameConfiguration->CapsActualSystem.GraphicsMode)
	{
	case MODE_TRIPLE:
		textprintf_ex(GameFramework->DrawingSurface,font,0,0,makecol(255,255,255),-1,"Triple Buffering. ");
		break;
	case MODE_PAGED:
		textprintf_ex(GameFramework->DrawingSurface,font,0,0,makecol(255,255,255),-1,"Page Flipping. ");
		break;
	default:
		textprintf_ex(GameFramework->DrawingSurface,font,0,0,makecol(255,255,255),-1,"Double Buffering. ");
		break;
	}
	if(GameConfiguration->CapsActualSystem.GraphicsMode==GameConfiguration->CapsGraphics.GraphicsMode)
		textprintf_ex(GameFramework->DrawingSurface,font,200,0,makecol(255,255,255),-1,"As requested");
	else
		textprintf_ex(GameFramework->DrawingSurface,font,200,0,makecol(255,255,255),-1,"Not As requested. See logfile.");

	textprintf_ex(GameFramework->DrawingSurface,font,0,10,makecol(255,255,255),-1,"1 Change to stop at end");
	textprintf_ex(GameFramework->DrawingSurface,font,0,20,makecol(255,255,255),-1,"2 Change to repeat ping pong");
	textprintf_ex(GameFramework->DrawingSurface,font,0,30,makecol(255,255,255),-1,"3 Change to repeat from start");
	textprintf_ex(GameFramework->DrawingSurface,font,0,40,makecol(255,255,255),-1,"4 Change to ping pong once");
	textprintf_ex(GameFramework->DrawingSurface,font,0,50,makecol(255,255,255),-1,"5 Change to there back and there again");
	textprintf_ex(GameFramework->DrawingSurface,font,0,60,makecol(255,255,255),-1,"6 Pause");
	textprintf_ex(GameFramework->DrawingSurface,font,0,70,makecol(255,255,255),-1,"7 Resume");
	textprintf_ex(GameFramework->DrawingSurface,font,0,80,makecol(255,255,255),-1,"8 Set To Auto Animation");
	textprintf_ex(GameFramework->DrawingSurface,font,0,90,makecol(255,255,255),-1,"9 Set To Manual Animation - press SPACE for next frame when set");
	textprintf_ex(GameFramework->DrawingSurface,font,0,100,makecol(255,255,255),-1,"0 Reset to normal");
	textprintf_ex(GameFramework->DrawingSurface,font,0,120,makecol(255,255,255),-1,"B Fade-out in 5 seconds (animation stays on and it works just as normal)");
	textprintf_ex(GameFramework->DrawingSurface,font,0,130,makecol(255,255,255),-1,"C Fade-in in 5 seconds (assumes at full hiddne - if not will take less time)");
	textprintf_ex(GameFramework->DrawingSurface,font,0,140,makecol(255,255,255),-1,"D Test Restarting System (e.g. change graphics mode)");
	textprintf_ex(GameFramework->DrawingSurface,font,0,150,makecol(255,255,255),-1,"+ Increase speed by 10ms");
	textprintf_ex(GameFramework->DrawingSurface,font,0,160,makecol(255,255,255),-1,"- Decrease speed by 10ms");
	textprintf_ex(GameFramework->DrawingSurface,font,0,170,makecol(255,255,255),-1,"F1 to F4 set countdown timer to random number of seconds");
	textprintf_ex(GameFramework->DrawingSurface,font,0,180,makecol(255,100,255),-1,"ESC to quit");
	textprintf_ex(GameFramework->DrawingSurface,font,0,190,makecol(255,100,255),-1,"Is End Animation Reached: %d",MainAnimation->ReadOnly_EndReached);
	textprintf_ex(GameFramework->DrawingSurface,font,0,200,makecol(255,100,255),-1,"Countdown reached: fade (%4d), C1(%4d), C2(%4d), C3(%4d), C4(%4d)",MainAnimation->ReadOnly_Timers[0],MainAnimation->ReadOnly_Timers[1],MainAnimation->ReadOnly_Timers[2],MainAnimation->ReadOnly_Timers[3],MainAnimation->ReadOnly_Timers[4]);
	textprintf_ex(GameFramework->DrawingSurface,font,0,210,makecol(255,100,255),-1,"Countdown value  : fade (%4.0f), C1(%4.0f), C2(%4.0f), C3(%4.0f), C4(%4.0f)",MainAnimation->ReadOnly_TimersCounter[0],MainAnimation->ReadOnly_TimersCounter[1],MainAnimation->ReadOnly_TimersCounter[2],MainAnimation->ReadOnly_TimersCounter[3],MainAnimation->ReadOnly_TimersCounter[4]);
	textprintf_ex(GameFramework->DrawingSurface,font,0,220,makecol(255,100,255),-1,"FPS: %d, Current Frame Speed Milliseconds: %d",TimerCurrentFPS,speed);
	textprintf_ex(GameFramework->DrawingSurface,font,200,230,makecol(255,255,255),-1,"s Change animation sprite");
	textprintf_ex(GameFramework->DrawingSurface,font,200,240,makecol(255,255,255),-1,"t Spawn lots of animations");
	textprintf_ex(GameFramework->DrawingSurface,font,501,211,makecol(255,255,255),-1,"Collision");
	textprintf_ex(GameFramework->DrawingSurface,font,501,468,makecol(255,255,255),-1,"Use Cursors");
}
